#ifndef TASK_MUTEX_H
#define TASK_MUTEX_H

#include "../noncopyable.h"
#include "CurrentThread.h"

#include <cassert>
#include <pthread.h>

/**
 * 基本的锁, 对需要加锁的资源需要在对应的类中声明对应的成员对象
 */
class MutexLock : muduo::noncopyable {
public:
    MutexLock() : holder_(0) {
        pthread_mutex_init(&mutex_, nullptr);
        // 初始化线程锁
    }

    virtual ~MutexLock() {
        assert(holder_ == 0);
        pthread_mutex_destroy(&mutex_);
    }

    bool isLockedByThisThread() {
        return holder_ == CurrentThread::getTid();
    }

    void assertLocked() {
        assert(isLockedByThisThread());
    }

    void lock() {
        pthread_mutex_lock(&mutex_);
        holder_ = CurrentThread::getTid();
        // 先加锁再设置对应的线程, 交换在并发环境可能会导致重复设置
    }

    void unlock() {
        holder_ = 0;
        pthread_mutex_unlock(&mutex_);
    }

    pthread_mutex_t *getPthreadMutex() {
        return &mutex_;
    }

private:
    friend class Condition;

    class UnassignGuard : noncopyable {
    public:
        explicit UnassignGuard(MutexLock &owner)
                : owner_(owner) {
            owner_.unassignHolder();
        }

        ~UnassignGuard() {
            owner_.assignHolder();
        }

    private:
        MutexLock &owner_;
    };

    void unassignHolder() {
        holder_ = 0;
    }

    void assignHolder() {
        holder_ = CurrentThread::getTid();
    }

    pthread_mutex_t mutex_;
    pid_t holder_;
};

/**
 * 互斥锁对象, 初始化使用对象资源中的MutexLock对象来加锁
 * 使用构造和析构来加锁和解锁
 */
class MutexLockGuard : muduo::noncopyable {
public:
    explicit MutexLockGuard(MutexLock &mutex) : mutex_(mutex) {
        mutex.lock();
    }

    virtual ~MutexLockGuard() {
        mutex_.unlock();
    }

private:
    MutexLock &mutex_;
};

#define MutexLockGuard(x) static_assert(flase, "missing mutex guard var name")
// 防止在使用的时候产生临时变量快速产生又释放了锁

/**
 * 条件变量, 基于锁来定义的
 */
class Condition : muduo::noncopyable {
public:
    explicit Condition(MutexLock &mutex) : mutex_(mutex) {
        pthread_cond_init(&pcond_, nullptr);
    }

    virtual ~Condition() {
        pthread_cond_destroy(&pcond_);
    }

    void wait() {
        pthread_cond_wait(&pcond_, mutex_.getPthreadMutex());
    }

    void notify() {
        pthread_cond_signal(&pcond_);
    }

    void notifyAll() {
        pthread_cond_broadcast(&pcond_);
    }

    /**
     * 等待固定的分钟数后苏醒
     * @param seconds 要等待的分钟数
     * @return 返回等待是否成功
     */
    bool waitForSeconds(double seconds);

private:
    MutexLock &mutex_;
    pthread_cond_t pcond_;
};


#endif //TASK_MUTEX_H
